1. GTZAN Dataset - Music Genre Classification 데이터셋¶
- 뮤직 장르의 Classification 을 위한 kaggle 데이터셋
인간이 이해하기 힘든 Spectogram의 y축을 Mel Scale로 변환한 것(Non-linear transformation)
Mel Scale : https://newsight.tistory.com/294
- .wav: 여러가지 장르의 형식의 오디오 파일
- .png: Mel Spectogram 이미지
- .csv: 각각의 오디오 features
1-1. 데이터셋 다운로드¶
import os
os.environ['KAGGLE_USERNAME'] = 'yunseopsong'
os.environ['KAGGLE_KEY'] = 'af83501befeb0bedb4e8100f474816a7'
!kaggle datasets download -d andradaolteanu/gtzan-dataset-music-genre-classification
Dataset URL: https://www.kaggle.com/datasets/andradaolteanu/gtzan-dataset-music-genre-classification License(s): other Downloading gtzan-dataset-music-genre-classification.zip to /content 100% 1.21G/1.21G [00:21<00:00, 59.2MB/s] 100% 1.21G/1.21G [00:21<00:00, 61.2MB/s]
!unzip -q /content/gtzan-dataset-music-genre-classification.zip
- filename: 파일 이름
- length: 길이
- chroma_stft_mean: 크로마 STFT 평균
- chroma_stft_var: 크로마 STFT 분산
- rms_mean: RMS 평균
- rms_var: RMS 분산
- spectral_centroid_mean: 스펙트럴 중심 주파수 평균
- spectral_centroid_var: 스펙트럴 중심 주파수 분산
- spectral_bandwidth_mean: 스펙트럴 대역폭 평균
- spectral_bandwidth_var: 스펙트럴 대역폭 분산
- rolloff_mean: 롤오프 평균
- rolloff_var: 롤오프 분산
- zero_crossing_rate_mean: 제로 크로싱 비율 평균
- zero_crossing_rate_var: 제로 크로싱 비율 분산
- harmony_mean: 하모니 평균
- harmony_var: 하모니 분산
- perceptr_mean: 퍼셉트르 평균
- perceptr_var: 퍼셉트르 분산
- tempo: 템포
- mfcc1_mean ~ mfcc20_mean: MFCC 1에서 20까지의 평균
- mfcc1_var ~ mfcc20_var: MFCC 1에서 20까지의 분산
- label: 레이블
2. 오디오 파일 이해하기¶
- y: 소리가 떨리는 세기(진폭)를 시간 순서대로 나열한 것
- Sampling rate: 1초당 샘플의 개수,단위 1초당 Hz 또는 KHz

2-2. librosa 패키지¶
- 음악 및 오디오 분석을 위한 Python 패키지입니다. 이는 음악 정보 검색 시스템을 만드는 데 필요한 빌딩 블록을 제공합니다.
- https://librosa.org/doc/latest/index.html
import librosa
# 음원파일 로드
y, sr = librosa.load('/content/Data/genres_original/jazz/jazz.00002.wav')
# y(소리의 세기) 값이 숫자로 이루어져있고, 음악의 길이 = 음파 길이/sr
print(y)
print(len(y))
print('Sampling rate (Hz): %d' %sr)
print('Audio length (seconds): %.2f' % (len(y) / sr)) #음악의 길이(초) = 음파의 길이/Sampling rate
[0.00335693 0.00491333 0.00378418 ... 0.05691528 0.05136108 0.03808594] 661794 Sampling rate (Hz): 22050 Audio length (seconds): 30.01
2-2.음악 들어보기¶
import IPython.display as ipd
ipd.Audio(y, rate=sr)
2-3. waveform 시각화¶
import matplotlib.pyplot as plt
import librosa.display
plt.figure(figsize =(16,6))
librosa.display.waveshow(y=y,sr=sr)
plt.show()
2-4. Fourier Transform(푸리에 변환)¶
입력 신호를 다양한 주파수를 가지는 주기 함수들로 분해하는 기법
시간 영역 데이터를 주파수 영역으로 변경 : time(시간) domain -> frequency(진동수) domain 변경 시 얻는 정보가 많아져 분석 용이.
y축: 주파수(로그 스케일)
color축:데시벨(진폭)

- Fast Fourier Transform(FFT) 은 Fourier Transform을 효율적으로 계산할 수 있는 알고리즘으로 Signal processing(신호 처리) 에 널리 사용
import numpy as np
D = np.abs(librosa.stft(y, n_fft=2048, hop_length=512)) #n_fft : window size / 이 때, 음성의 길이를 얼마만큼으로 자를 것인가? 를 window라고 부른다.
print(D.shape)
plt.figure(figsize=(16,6))
plt.plot(D)
plt.show()
(1025, 1293)
2-5.Spectogram(스펙토그램)¶
시간에 따라 변하는 신호를 non periodic(비주기적) 신호 라고한다
푸리에 변환을 통해 특정 길이의 음성 조각이 각각의 주파수 성분을 얼마만큼 갖고 있는지를 의미하는 스펙트럼을 얻을 수 있다
FFT가 계속 쌓인 FFT 묶음으로 생각할 수 있다
시간이 지남에 따라 서로 다른 주파수에서 달라지는 신호의 크기 또는 진폭을 시각적으로 나타내는 방법
음성에 들어있는 정보들을 수학적인 신호 처리를 거쳐 추출할 수 있다
여러 개의 스펙트럼을 시간 축에 나열하면 시간 변화에 따른 스펙트럼의 변화인 스펙트로그램이 시각화 된다

DB = librosa.amplitude_to_db(D, ref=np.max)
# amplitude(진폭) -> DB(데시벨)로 바꿈
plt.figure(figsize=(16,6))
librosa.display.specshow(DB,sr=sr, hop_length=512, x_axis='time', y_axis='log')
plt.colorbar()
plt.show()
2-6. Mel Spectogram¶
인간은 주파수를 linear scale (선형 척도) 로 인식하지 못함
인간은 높은 주파수보다 낮은 주파수에서의 차이를 더 잘 감지
- 예) 500Hz와 1000Hz의 차이는 쉽게 구분할 수 있지만 두 쌍 사이의 거리가 같더라도 10,000Hz와 10,500Hz의 차이는 거의 구분할 수 없슴
1937년에 Stevens, Volkmann, Newmann은 pitch 의 동일한 거리가 청취자에게 동일한 거리에서 들리도록 하는 pitch 단위를 제안
주파수에 대한 수학적 연산을 수행하여 주파수를 mel scale 로 변환

S = librosa.feature.melspectrogram(y=y, sr=sr)
S_DB = librosa.amplitude_to_db(S, ref=np.max)
plt.figure(figsize=(16,6))
librosa.display.specshow(S_DB, sr=sr,hop_length=512, x_axis='time',y_axis='log')
plt.colorbar(format='%+2.0f dB')
plt.title('Mel Spectrogram')
plt.show()
2-6. jazz와 blues의 Mel Spectogram 비교¶
y, sr = librosa.load('/content/Data/genres_original/blues/blues.00002.wav')
y, _ = librosa.effects.trim(y)
S = librosa.feature.melspectrogram(y=y, sr=sr)
S_DB = librosa.amplitude_to_db(S, ref=np.max)
plt.figure(figsize=(16,6))
librosa.display.specshow(S_DB, sr=sr,hop_length=512, x_axis='time',y_axis='log')
plt.colorbar(format='%+2.0f dB')
plt.title('Mel Spectrogram')
plt.show()
y1, sr1 = librosa.load('/content/Data/genres_original/jazz/jazz.00002.wav')
y1, _ = librosa.effects.trim(y1)
S1 = librosa.feature.melspectrogram(y=y1, sr=sr1)
S_DB1 = librosa.amplitude_to_db(S1, ref=np.max)
y2, sr2 = librosa.load('/content/Data/genres_original/blues/blues.00002.wav')
y2, _ = librosa.effects.trim(y2)
S2 = librosa.feature.melspectrogram(y=y2, sr=sr2)
S_DB2 = librosa.amplitude_to_db(S2, ref=np.max)
# 그래프 그리기
plt.figure(figsize=(16, 6))
# 첫 번째 Mel Spectrogram 그래프
plt.subplot(1, 2, 1)
librosa.display.specshow(S_DB1, sr=sr1, hop_length=512, x_axis='time', y_axis='log')
plt.colorbar(format='%+2.0f dB')
plt.title('Jazz')
# 두 번째 Mel Spectrogram 그래프
plt.subplot(1, 2, 2)
librosa.display.specshow(S_DB2, sr=sr2, hop_length=512, x_axis='time', y_axis='log')
plt.colorbar(format='%+2.0f dB')
plt.title('Blues')
plt.tight_layout()
plt.show()
차이점:¶
주파수 대역의 활용:
- 재즈 (왼쪽 이미지): 주파수 대역이 넓게 사용되며, 특히 중고주파수 영역(1000Hz 이상)에서 뚜렷한 패턴이 보입니다. 이는 재즈 음악이 다양한 악기와 복잡한 하모니를 포함하기 때문입니다 .
- 블루스 (오른쪽 이미지): 주파수 대역이 주로 중저주파수 영역(2000Hz 이하)에 집중되어 있습니다. 이는 블루스 음악이 주로 보컬과 기타 같은 중저음악기를 사용하기 때문입니다.
시간에 따른 주파수 변화:
재즈: 시간에 따라 주파수 스펙트럼이 다양하게 변하며, 다양한 리듬과 멜로디 라인이 교차합니다. 이는 재즈가 즉흥 연주와 복잡한 구조를 가지는 특징을 반영합니다.
블루스: 주파수 변화가 비교적 일정하며 반복적인 패턴을 보입니다. 이는 블루스가 12마디 블루스와 같은 반복적인 구조를 가지는 특징을 반영합니다.
스펙트로그램의 밀도:
재즈: 스펙트로그램이 더 밀집되어 있으며, 고주파 영역에서도 많은 에너지를 가지고 있습니다. 이는 재즈 음악에서 빠른 템포와 복잡한 리듬을 나타냅니다.
블루스: 스펙트로그램이 비교적 덜 밀집되어 있으며, 중저주파 영역에 에너지가 집중됩니다. 이는 블루스 음악이 더 느리고 단순한 리듬을 가지고 있음을 나타냅니다.
요약:
재즈 음악: 주파수 대역이 넓게 사용되며, 고주파 영역에서도 에너지가 많고, 시간에 따라 주파수 변화가 복잡합니다.
블루스 음악: 주로 중저주파수 대역에 에너지가 집중되며, 반복적인 패턴과 일정한 주파수 변화를 보입니다.
3. 오디오 특성 추출(Audio Feature Extraction)¶
3-1. Tempo(BPM)¶
- librosa.beat.beat_track 함수는 템포(beat per minute, BPM)와 Beat의 타임스탬프(오디오 신호에서 감지된 비트의 시간 위치를 나타내는 배열) 두 개의 값을 반환
tempo ,beats = librosa.beat.beat_track(y=y,sr=sr)
print(tempo)
print(beats)
[161.49902344] [ 46 64 80 96 112 129 146 163 179 196 212 228 244 261 278 295 311 327 343 360 376 393 410 427 443 460 476 492 508 524 541 558 575 591 607 623 639 656 673 689 705 722 739 755 771 788 805 822 838 856 872 888 904 921 938 954 970 986 1002 1018 1034 1050 1064 1079 1095 1111 1127 1143 1158 1175 1192 1209 1224 1238 1255]
3-2. 제로 교차율(Zero Crossing Rate)¶
음파가 양에서 음으로 또는 음에서 양으로 바뀌는 비율
간단하지만 많이 쓰인다.
zero_crossings = librosa.zero_crossings(y, pad=False)
print(zero_crossings)
print(sum(zero_crossings)) # 음 <-> 양 이동한 횟수
[False False False ... False True False] 50563
# 10000~ 10040 사이 값을 살펴보기.
# 이 때 Zero Crossing은 0이 되는 선을 지나친 횟수를 의미
n0 = 10000
n1 = 10040
plt.figure(figsize=(16,6))
plt.plot(y[n0:n1])
plt.grid()
plt.show()
# Zero Crossing 횟수
zero_crossings = librosa.zero_crossings(y[n0:n1], pad=False) #n0 ~ n1 사이 zero crossings
print('Zero Crossing 횟수:',sum(zero_crossings))
Zero Crossing 횟수: 5
3-3. Harmonic and Percussive Components¶
Harmonic Components (하모닉 구성 요소)
하모닉 구성 요소는 음악의 주파수 스펙트럼에서 기본 주파수와 그 배수(고조파)로 나타나는 성분입니다.
이 성분들은 주로 악기의 음색(Timbre)과 연관이 있습니다.
하모닉 구성 요소는 다음과 같은 특징을 가집니다:
음악의 색깔(Timbre): 하모닉 성분은 음색을 형성하여 각 악기의 고유한 소리를 만듭니다. 예를 들어, 피아노와 바이올린이 같은 음을 연주하더라도 그 소리가 다르게 들리는 이유는 각 악기의 하모닉 성분이 다르기 때문입니다.
주기적 패턴: 하모닉 성분은 주기적이며, 주파수 영역에서 명확한 피크를 보입니다. 이는 사람이 음정을 인식할 수 있게 합니다.
연속적 특징: 하모닉 성분은 일반적으로 시간에 따라 비교적 연속적이며, 부드러운 변화를 나타냅니다.
Percussive Components (퍼커시브 구성 요소)
퍼커시브 구성 요소는 리듬과 감정을 나타내는 충격파 형태의 성분입니다.
이 성분들은 주로 타악기와 같은 소리를 형성하며, 다음과 같은 특징을 가집니다:
리듬과 박자: 퍼커시브 성분은 리듬과 박자를 결정합니다. 예를 들어, 드럼의 비트나 스네어 드럼의 타격 소리는 퍼커시브 성분입니다.
비주기적 패턴: 퍼커시브 성분은 비주기적이며, 짧고 강한 에너지를 가집니다. 이는 음악에서 충격적이거나 강조된 소리를 형성합니다.
비연속적 특징: 퍼커시브 성분은 시간에 따라 급격한 변화를 나타내며, 순간적인 에너지를 방출합니다.
# 하모닉 및 퍼커시브 성분 분리
y_harmonic, y_percussive = librosa.effects.hpss(y)
# 하모닉 성분 시각화
plt.figure(figsize=(16, 6))
plt.subplot(2, 1, 1)
librosa.display.waveshow(y_harmonic, sr=sr, alpha=0.6)
plt.title('Harmonic Component')
plt.xlabel('Time (s)')
plt.ylabel('Amplitude')
# 퍼커시브 성분 시각화
plt.subplot(2, 1, 2)
librosa.display.waveshow(y_percussive, sr=sr, alpha=0.6)
plt.title('Percussive Component')
plt.xlabel('Time (s)')
plt.ylabel('Amplitude')
plt.tight_layout()
plt.show()
plt.figure(figsize=(16,6))
plt.plot(y_harmonic, color='b')
plt.plot(y_percussive, color='r')
plt.show()
3-4.Spectral Centroid¶
소리를 주파수 표현했을 때, 주파수의 가중평균을 계산하여 소리의 "무게 중심"이 어딘지를 알려주는 지표
예를 들어, 블루스 음악은 무게 중심이 가운데 부분에 놓여있는 반면, 메탈 음악은 (끝 부분에서 달리기 때문에) 노래의 마지막 부분에 무게 중심이 실린다.
# 스펙트럴 센트로이드 계산
spectral_centroids = librosa.feature.spectral_centroid(y=y, sr=sr)[0]
# 시각화를 위한 시간 변수 계산
frames = range(len(spectral_centroids))
# 프레임 수를 시간(초)으로 변환
t = librosa.frames_to_time(frames)
# 정규화 함수 정의
def normalize(x, axis=0):
return sklearn.preprocessing.minmax_scale(x, axis=axis)
# 시각화
plt.figure(figsize=(16,6))
# librosa.display.waveshow: 오디오 신호를 파형(waveform)으로 시각화
librosa.display.waveshow(y=y, sr=sr, alpha=0.5, color='b')
# 시간 t를 x축으로, 정규화된 스펙트럴 센트로이드를 y축으로 설정하여
# 스펙트럴 센트로이드를 빨간색 선으로 시각화합니다
plt.plot(t, normalize(spectral_centroids), color='r')
plt.show()
스펙트럴 센트로이드 분석¶
그래프에서 빨간색 선으로 나타난 스펙트럴 센트로이드는 시간에 따른 주파수 스펙트럼의 무게 중심을 시각적으로 보여줍니다.
초기 부분 (0~5초):
스펙트럴 센트로이드가 급격히 증가하는 부분이 있습니다. 이 구간에서는 고주파 성분이 많음을 의미합니다. 따라서 이 구간의 소리는 상대적으로 밝고 경쾌하게 들릴 수 있습니다.
중간 부분 (5~20초):
스펙트럴 센트로이드가 비교적 일정하고 중간 정도의 값을 유지하고 있습니다. 이 구간에서는 저주파와 고주파 성분이 균형 있게 분포되어 있음을 나타냅니다. 따라서 이 구간의 소리는 중간 정도의 밝기를 가지고 있을 가능성이 큽니다.
후기 부분 (20초 이후):
스펙트럴 센트로이드가 여전히 일정하고 중간 정도의 값을 유지합니다. 이 구간에서도 저주파와 고주파 성분이 균형 있게 분포되어 있음을 나타냅니다. 종합적인 무게 중심 전체적으로 볼 때, 소리의 무게 중심은 중간 정도의 주파수 영역에 위치해 있습니다. 초기 구간을 제외하면 스펙트럴 센트로이드는 비교적 일정하게 유지되므로, 이 소리는 특정한 고주파 또는 저주파 성분에 치우치지 않고 균형 잡힌 주파수 분포를 가지고 있음을 알 수 있습니다.
종합적인 무게 중심¶
전체적으로 볼 때, 소리의 무게 중심은 중간 정도의 주파수 영역에 위치해 있습니다. 초기 구간을 제외하면 스펙트럴 센트로이드는 비교적 일정하게 유지되므로, 이 소리는 특정한 고주파 또는 저주파 성분에 치우치지 않고 균형 잡힌 주파수 분포를 가지고 있음을 알 수 있습니다.
만약 특정 구간에서 소리의 무게 중심을 더 정확하게 알고 싶다면, 스펙트럴 센트로이드 값을 평균 내거나 특정 구간의 값을 분석하여 소리의 무게 중심이 어느 주파수 대역에 위치해 있는지 파악할 수 있습니다.
# 확인
import IPython.display as ipd
ipd.Audio(y, rate=sr)
3-5. Spectral Rolloff¶
신호 모양을 측정한다.
총 스펙트럴 에너지 중 낮은 주파수(85% 이하)에 얼마나 많이 집중되어 있는가
spectral_rolloff = librosa.feature.spectral_rolloff(y=y, sr=sr)[0]
plt.figure(figsize=(16,6))
librosa.display.waveshow(y=y,sr=sr,alpha=0.5,color='b')
plt.plot(t, normalize(spectral_rolloff),color='r')
plt.show()
3-6. Mel-Frequency Cepstral Coefficients(MFCCs)¶
MFCCs는 특징들의 작은 집합(약 10-20)으로 스펙트럴 포곡선의 전체적인 모양을 축약하여 보여준다.
사람의 청각 구조를 반영하여 음성 정보 추출
MFCC 그래프를 보면 각 MFCC 계수가 시간에 따라 어떻게 변하는지 시각적으로 확인할 수 있다.
일반적으로 음성의 다양한 발음이나 소리 특성에 따라 MFCC 계수의 패턴이 달라집니다.
mfccs = librosa.feature.mfcc(y=y, sr=sr)
mfccs = normalize(mfccs,axis=1)
print('mean: %.2f' % mfccs.mean())
print('var: %.2f' % mfccs.var())
plt.figure(figsize=(16, 6))
librosa.display.specshow(mfccs, sr=sr, x_axis='time', y_axis='frames')
plt.colorbar()
plt.title('MFCC')
plt.show()
mean: 0.52 var: 0.03
3-7. 크로마 특징(Chroma features)¶
음악에서 매우 중요한 역할을 하는 특징입니다. 여러 가지 음악적 분석 및 처리 작업에서 사용됨
강렬하고 흥미로운 표현: 크로마 특징은 음악의 감정적 표현이나 음악적 특성을 감지하는 데 도움을 줍니다. 특히 화음 인식과 같은 작업에서 중요한 역할을 합니다.
크로마 개념의 기반: 크로마 특징은 인간 청각의 특성을 기반으로 합니다. 인간은 주파수가 옥타브(2배) 차이가 나는 두 음을 유사한 음으로 인식하는 경향이 있습니다. 예를 들어, 동일한 음계의 다른 옥타브의 음들은 유사성을 가지고 있습니다.
12개의 Bin으로 표현: 크로마 특징은 전체 주파수 스펙트럼을 12개의 Bin으로 나누어 표현합니다. 이는 음악에서 사용되는 12개의 서로 다른 반음(세미톤)을 나타내며, 이러한 각 Bin은 주어진 시점에서 음악적 특성을 나타냅니다.
크로마와 반음: 크로마는 음악에서 반음(세미톤)의 개념과 밀접하게 연결됩니다. 12개의 Bin은 옥타브 내에서 반음 간격을 나타내며, 이는 음악에서 사용되는 다양한 음계나 화음의 구성을 이해하는 데 중요합니다.
크로마 특징은 주로 음악 정보 검색(Music Information Retrieval, MIR) 및 음악 신호 처리에서 사용되며, 음악적 구조를 분석하고 이해하는 데 중요한 도구로 인정받고 있습니다.
![]()
chromagram = librosa.feature.chroma_stft(y=y, sr=sr, hop_length=512)
plt.figure(figsize=(16,6))
librosa.display.specshow(chromagram,x_axis='time', y_axis='chroma', hop_length=512)
plt.show()
4. 음악장르 분류 알고리즘¶
import pandas as pd
df = pd.read_csv('/content/Data/features_3_sec.csv')
df.head()
| filename | length | chroma_stft_mean | chroma_stft_var | rms_mean | rms_var | spectral_centroid_mean | spectral_centroid_var | spectral_bandwidth_mean | spectral_bandwidth_var | ... | mfcc16_var | mfcc17_mean | mfcc17_var | mfcc18_mean | mfcc18_var | mfcc19_mean | mfcc19_var | mfcc20_mean | mfcc20_var | label | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | blues.00000.0.wav | 66149 | 0.335406 | 0.091048 | 0.130405 | 0.003521 | 1773.065032 | 167541.630869 | 1972.744388 | 117335.771563 | ... | 39.687145 | -3.241280 | 36.488243 | 0.722209 | 38.099152 | -5.050335 | 33.618073 | -0.243027 | 43.771767 | blues |
| 1 | blues.00000.1.wav | 66149 | 0.343065 | 0.086147 | 0.112699 | 0.001450 | 1816.693777 | 90525.690866 | 2010.051501 | 65671.875673 | ... | 64.748276 | -6.055294 | 40.677654 | 0.159015 | 51.264091 | -2.837699 | 97.030830 | 5.784063 | 59.943081 | blues |
| 2 | blues.00000.2.wav | 66149 | 0.346815 | 0.092243 | 0.132003 | 0.004620 | 1788.539719 | 111407.437613 | 2084.565132 | 75124.921716 | ... | 67.336563 | -1.768610 | 28.348579 | 2.378768 | 45.717648 | -1.938424 | 53.050835 | 2.517375 | 33.105122 | blues |
| 3 | blues.00000.3.wav | 66149 | 0.363639 | 0.086856 | 0.132565 | 0.002448 | 1655.289045 | 111952.284517 | 1960.039988 | 82913.639269 | ... | 47.739452 | -3.841155 | 28.337118 | 1.218588 | 34.770935 | -3.580352 | 50.836224 | 3.630866 | 32.023678 | blues |
| 4 | blues.00000.4.wav | 66149 | 0.335579 | 0.088129 | 0.143289 | 0.001701 | 1630.656199 | 79667.267654 | 1948.503884 | 60204.020268 | ... | 30.336359 | 0.664582 | 45.880913 | 1.689446 | 51.363583 | -3.392489 | 26.738789 | 0.536961 | 29.146694 | blues |
5 rows × 60 columns
df.info()
<class 'pandas.core.frame.DataFrame'> RangeIndex: 9990 entries, 0 to 9989 Data columns (total 60 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 filename 9990 non-null object 1 length 9990 non-null int64 2 chroma_stft_mean 9990 non-null float64 3 chroma_stft_var 9990 non-null float64 4 rms_mean 9990 non-null float64 5 rms_var 9990 non-null float64 6 spectral_centroid_mean 9990 non-null float64 7 spectral_centroid_var 9990 non-null float64 8 spectral_bandwidth_mean 9990 non-null float64 9 spectral_bandwidth_var 9990 non-null float64 10 rolloff_mean 9990 non-null float64 11 rolloff_var 9990 non-null float64 12 zero_crossing_rate_mean 9990 non-null float64 13 zero_crossing_rate_var 9990 non-null float64 14 harmony_mean 9990 non-null float64 15 harmony_var 9990 non-null float64 16 perceptr_mean 9990 non-null float64 17 perceptr_var 9990 non-null float64 18 tempo 9990 non-null float64 19 mfcc1_mean 9990 non-null float64 20 mfcc1_var 9990 non-null float64 21 mfcc2_mean 9990 non-null float64 22 mfcc2_var 9990 non-null float64 23 mfcc3_mean 9990 non-null float64 24 mfcc3_var 9990 non-null float64 25 mfcc4_mean 9990 non-null float64 26 mfcc4_var 9990 non-null float64 27 mfcc5_mean 9990 non-null float64 28 mfcc5_var 9990 non-null float64 29 mfcc6_mean 9990 non-null float64 30 mfcc6_var 9990 non-null float64 31 mfcc7_mean 9990 non-null float64 32 mfcc7_var 9990 non-null float64 33 mfcc8_mean 9990 non-null float64 34 mfcc8_var 9990 non-null float64 35 mfcc9_mean 9990 non-null float64 36 mfcc9_var 9990 non-null float64 37 mfcc10_mean 9990 non-null float64 38 mfcc10_var 9990 non-null float64 39 mfcc11_mean 9990 non-null float64 40 mfcc11_var 9990 non-null float64 41 mfcc12_mean 9990 non-null float64 42 mfcc12_var 9990 non-null float64 43 mfcc13_mean 9990 non-null float64 44 mfcc13_var 9990 non-null float64 45 mfcc14_mean 9990 non-null float64 46 mfcc14_var 9990 non-null float64 47 mfcc15_mean 9990 non-null float64 48 mfcc15_var 9990 non-null float64 49 mfcc16_mean 9990 non-null float64 50 mfcc16_var 9990 non-null float64 51 mfcc17_mean 9990 non-null float64 52 mfcc17_var 9990 non-null float64 53 mfcc18_mean 9990 non-null float64 54 mfcc18_var 9990 non-null float64 55 mfcc19_mean 9990 non-null float64 56 mfcc19_var 9990 non-null float64 57 mfcc20_mean 9990 non-null float64 58 mfcc20_var 9990 non-null float64 59 label 9990 non-null object dtypes: float64(57), int64(1), object(2) memory usage: 4.6+ MB
- filename: 파일 이름
- length: 길이
- chroma_stft_mean: 크로마 STFT 평균
- chroma_stft_var: 크로마 STFT 분산
- rms_mean: RMS 평균
- rms_var: RMS 분산
- spectral_centroid_mean: 스펙트럴 중심 주파수 평균
- spectral_centroid_var: 스펙트럴 중심 주파수 분산
- spectral_bandwidth_mean: 스펙트럴 대역폭 평균
- spectral_bandwidth_var: 스펙트럴 대역폭 분산
- rolloff_mean: 롤오프 평균
- rolloff_var: 롤오프 분산
- zero_crossing_rate_mean: 제로 크로싱 비율 평균
- zero_crossing_rate_var: 제로 크로싱 비율 분산
- harmony_mean: 하모니 평균
- harmony_var: 하모니 분산
- perceptr_mean: 퍼셉트르 평균
- perceptr_var: 퍼셉트르 분산
- tempo: 템포
- mfcc1_mean ~ mfcc20_mean: MFCC 1에서 20까지의 평균
- mfcc1_var ~ mfcc20_var: MFCC 1에서 20까지의 분산
- label: 레이블
df.isna().mean() # NaN 확인
filename 0.0 length 0.0 chroma_stft_mean 0.0 chroma_stft_var 0.0 rms_mean 0.0 rms_var 0.0 spectral_centroid_mean 0.0 spectral_centroid_var 0.0 spectral_bandwidth_mean 0.0 spectral_bandwidth_var 0.0 rolloff_mean 0.0 rolloff_var 0.0 zero_crossing_rate_mean 0.0 zero_crossing_rate_var 0.0 harmony_mean 0.0 harmony_var 0.0 perceptr_mean 0.0 perceptr_var 0.0 tempo 0.0 mfcc1_mean 0.0 mfcc1_var 0.0 mfcc2_mean 0.0 mfcc2_var 0.0 mfcc3_mean 0.0 mfcc3_var 0.0 mfcc4_mean 0.0 mfcc4_var 0.0 mfcc5_mean 0.0 mfcc5_var 0.0 mfcc6_mean 0.0 mfcc6_var 0.0 mfcc7_mean 0.0 mfcc7_var 0.0 mfcc8_mean 0.0 mfcc8_var 0.0 mfcc9_mean 0.0 mfcc9_var 0.0 mfcc10_mean 0.0 mfcc10_var 0.0 mfcc11_mean 0.0 mfcc11_var 0.0 mfcc12_mean 0.0 mfcc12_var 0.0 mfcc13_mean 0.0 mfcc13_var 0.0 mfcc14_mean 0.0 mfcc14_var 0.0 mfcc15_mean 0.0 mfcc15_var 0.0 mfcc16_mean 0.0 mfcc16_var 0.0 mfcc17_mean 0.0 mfcc17_var 0.0 mfcc18_mean 0.0 mfcc18_var 0.0 mfcc19_mean 0.0 mfcc19_var 0.0 mfcc20_mean 0.0 mfcc20_var 0.0 label 0.0 dtype: float64
X = df.drop(columns=['filename','length','label'])
y = df['label']
scaler = sklearn.preprocessing.MinMaxScaler()
np_scaled = scaler.fit_transform(X)
X = pd.DataFrame(np_scaled, columns=X.columns)
print(X.nunique())
X.head()
chroma_stft_mean 9845 chroma_stft_var 9831 rms_mean 9846 rms_var 9846 spectral_centroid_mean 9847 spectral_centroid_var 9847 spectral_bandwidth_mean 9847 spectral_bandwidth_var 9847 rolloff_mean 9181 rolloff_var 9847 zero_crossing_rate_mean 8783 zero_crossing_rate_var 9846 harmony_mean 9847 harmony_var 9847 perceptr_mean 9847 perceptr_var 9846 tempo 61 mfcc1_mean 9845 mfcc1_var 9846 mfcc2_mean 9838 mfcc2_var 9845 mfcc3_mean 9847 mfcc3_var 9842 mfcc4_mean 9844 mfcc4_var 9840 mfcc5_mean 9845 mfcc5_var 9844 mfcc6_mean 9844 mfcc6_var 9843 mfcc7_mean 9847 mfcc7_var 9844 mfcc8_mean 9847 mfcc8_var 9847 mfcc9_mean 9846 mfcc9_var 9843 mfcc10_mean 9847 mfcc10_var 9845 mfcc11_mean 9846 mfcc11_var 9846 mfcc12_mean 9847 mfcc12_var 9844 mfcc13_mean 9847 mfcc13_var 9847 mfcc14_mean 9846 mfcc14_var 9846 mfcc15_mean 9846 mfcc15_var 9842 mfcc16_mean 9847 mfcc16_var 9845 mfcc17_mean 9847 mfcc17_var 9847 mfcc18_mean 9847 mfcc18_var 9845 mfcc19_mean 9844 mfcc19_var 9843 mfcc20_mean 9846 mfcc20_var 9847 dtype: int64
| chroma_stft_mean | chroma_stft_var | rms_mean | rms_var | spectral_centroid_mean | spectral_centroid_var | spectral_bandwidth_mean | spectral_bandwidth_var | rolloff_mean | rolloff_var | ... | mfcc16_mean | mfcc16_var | mfcc17_mean | mfcc17_var | mfcc18_mean | mfcc18_var | mfcc19_mean | mfcc19_var | mfcc20_mean | mfcc20_var | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 0.355399 | 0.716757 | 0.293133 | 0.107955 | 0.262173 | 0.034784 | 0.459205 | 0.094130 | 0.346153 | 0.083164 | ... | 0.363613 | 0.056198 | 0.397172 | 0.066062 | 0.371828 | 0.055344 | 0.380831 | 0.026797 | 0.506746 | 0.047781 |
| 1 | 0.367322 | 0.670347 | 0.253040 | 0.044447 | 0.270969 | 0.018716 | 0.470831 | 0.052261 | 0.363722 | 0.051694 | ... | 0.468596 | 0.092912 | 0.351681 | 0.074001 | 0.362068 | 0.076365 | 0.418452 | 0.082414 | 0.593029 | 0.065548 |
| 2 | 0.373159 | 0.728067 | 0.296753 | 0.141663 | 0.265293 | 0.023073 | 0.494051 | 0.059922 | 0.378215 | 0.060820 | ... | 0.479681 | 0.096704 | 0.420979 | 0.050639 | 0.400536 | 0.067509 | 0.433742 | 0.043841 | 0.546264 | 0.036062 |
| 3 | 0.399349 | 0.677066 | 0.298024 | 0.075042 | 0.238427 | 0.023187 | 0.455246 | 0.066234 | 0.329587 | 0.070906 | ... | 0.386258 | 0.067995 | 0.387474 | 0.050617 | 0.380430 | 0.050030 | 0.405824 | 0.041898 | 0.562204 | 0.034873 |
| 4 | 0.355668 | 0.689113 | 0.322308 | 0.052149 | 0.233460 | 0.016451 | 0.451651 | 0.047830 | 0.318453 | 0.046916 | ... | 0.438567 | 0.042500 | 0.460314 | 0.083860 | 0.388590 | 0.076524 | 0.409019 | 0.020763 | 0.517913 | 0.031713 |
5 rows × 57 columns
# label 이 문자열 이므로 0 ~ 9 까지의 숫자형으로 변환
from sklearn.preprocessing import LabelEncoder
label_encoder = LabelEncoder()
y = label_encoder.fit_transform(y)
print(y)
[0 0 0 ... 9 9 9]
from sklearn.model_selection import train_test_split
X_train , X_test , y_train, y_test = train_test_split(X,y , test_size=0.2, random_state=2024)
print(X_train.shape, y_train.shape)
print(X_test.shape, y_test.shape)
(7992, 57) (7992,) (1998, 57) (1998,)
XGBoost 머신러닝 사용¶
from xgboost import XGBClassifier
from sklearn.metrics import accuracy_score
xgb = XGBClassifier(n_estimators=1000, learning_rate=0.05)
xgb.fit(X_train, y_train) #학습
y_preds = xgb.predict(X_test) #검증
print('Accuracy: %.2f' % accuracy_score(y_test,y_preds))
Accuracy: 0.91
ResNet모델 사용¶
import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import TensorDataset, DataLoader, random_split
from torchvision import transforms, models, datasets
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
print(device)
cpu
# Tensor로 변환
X_train_tensor = torch.tensor(X_train.values, dtype=torch.float32)
y_train_tensor = torch.tensor(y_train, dtype=torch.long)
X_test_tensor = torch.tensor(X_test.values, dtype=torch.float32)
y_test_tensor = torch.tensor(y_test, dtype=torch.long)
# 데이터셋 설정
train_dataset = TensorDataset(X_train_tensor, y_train_tensor)
test_dataset = TensorDataset(X_test_tensor, y_test_tensor)
# 데이터로더 설정
batch_size = 32
train_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True)
test_loader = DataLoader(test_dataset, batch_size=batch_size, shuffle=False)
dataloaders = {
'train': train_loader,
'test': test_loader
}
# 설정
class CustomResNet(nn.Module):
def __init__(self, input_size, num_classes):
super(CustomResNet, self).__init__()
self.resnet = models.resnet18(pretrained=True)
self.resnet.conv1 = nn.Conv2d(1, 64, kernel_size=(7, 7), stride=(2, 2), padding=(3, 3), bias=False)
self.resnet.fc = nn.Linear(self.resnet.fc.in_features, num_classes)
def forward(self, x):
x = x.unsqueeze(1).unsqueeze(1) # [batch_size, 1, 1, input_size]
x = self.resnet(x)
return x
input_size = X_train.shape[1]
num_classes = len(label_encoder.classes_)
model = CustomResNet(input_size, num_classes).to(device)
# 손실 함수 및 옵티마이저 설정
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)
# 학습
epochs = 10
for epoch in range(epochs):
model.train()
sum_losses = 0
sum_accs = 0
for x_batch, y_batch in train_loader:
x_batch = x_batch.to(device)
y_batch = y_batch.to(device)
y_pred = model(x_batch)
loss = criterion(y_pred, y_batch)
optimizer.zero_grad()
loss.backward()
optimizer.step()
sum_losses += loss.item()
y_pred_index = torch.argmax(y_pred, axis=1)
acc = (y_batch == y_pred_index).float().mean().item() * 100
sum_accs += acc
avg_loss = sum_losses / len(train_loader)
avg_acc = sum_accs / len(train_loader)
print(f'Train: Epoch {epoch+1}/{epochs} Loss: {avg_loss:.4f} Accuracy: {avg_acc:.2f}%')
# 검증
model.eval()
with torch.no_grad():
sum_losses = 0
sum_accs = 0
for x_batch, y_batch in test_loader:
x_batch = x_batch.to(device)
y_batch = y_batch.to(device)
y_pred = model(x_batch)
loss = criterion(y_pred, y_batch)
sum_losses += loss.item()
y_pred_index = torch.argmax(y_pred, axis=1)
acc = (y_batch == y_pred_index).float().mean().item() * 100
sum_accs += acc
avg_loss = sum_losses / len(test_loader)
avg_acc = sum_accs / len(test_loader)
print(f'Test: Epoch {epoch+1}/{epochs} Loss: {avg_loss:.4f} Accuracy: {avg_acc:.2f}%')
/usr/local/lib/python3.10/dist-packages/torchvision/models/_utils.py:208: UserWarning: The parameter 'pretrained' is deprecated since 0.13 and may be removed in the future, please use 'weights' instead. warnings.warn( /usr/local/lib/python3.10/dist-packages/torchvision/models/_utils.py:223: UserWarning: Arguments other than a weight enum or `None` for 'weights' are deprecated since 0.13 and may be removed in the future. The current behavior is equivalent to passing `weights=ResNet18_Weights.IMAGENET1K_V1`. You can also use `weights=ResNet18_Weights.DEFAULT` to get the most up-to-date weights. warnings.warn(msg)
Train: Epoch 1/10 Loss: 1.4128 Accuracy: 51.41% Test: Epoch 1/10 Loss: 1.0071 Accuracy: 65.39% Train: Epoch 2/10 Loss: 1.1018 Accuracy: 62.02% Test: Epoch 2/10 Loss: 1.1884 Accuracy: 56.15% Train: Epoch 3/10 Loss: 0.9515 Accuracy: 67.19% Test: Epoch 3/10 Loss: 1.1428 Accuracy: 59.81% Train: Epoch 4/10 Loss: 0.8121 Accuracy: 72.75% Test: Epoch 4/10 Loss: 0.9976 Accuracy: 65.36% Train: Epoch 5/10 Loss: 0.7107 Accuracy: 75.57% Test: Epoch 5/10 Loss: 0.8159 Accuracy: 73.19% Train: Epoch 6/10 Loss: 0.6307 Accuracy: 78.68% Test: Epoch 6/10 Loss: 0.8050 Accuracy: 73.04% Train: Epoch 7/10 Loss: 0.5578 Accuracy: 81.05% Test: Epoch 7/10 Loss: 0.5901 Accuracy: 79.69% Train: Epoch 8/10 Loss: 0.4807 Accuracy: 83.28% Test: Epoch 8/10 Loss: 0.5898 Accuracy: 79.47% Train: Epoch 9/10 Loss: 0.4148 Accuracy: 86.22% Test: Epoch 9/10 Loss: 0.5107 Accuracy: 83.39% Train: Epoch 10/10 Loss: 0.3663 Accuracy: 87.45% Test: Epoch 10/10 Loss: 0.5031 Accuracy: 83.21%
import librosa
import numpy as np
import pandas as pd
def predict_genre_from_audio(audio_file, model, label_encoder, scaler):
# 오디오 파일 불러오기
y, sr = librosa.load(audio_file)
# 특성 추출
chroma_stft = librosa.feature.chroma_stft(y=y, sr=sr)
rms = librosa.feature.rms(y=y)
spectral_centroid = librosa.feature.spectral_centroid(y=y, sr=sr)
spectral_bandwidth = librosa.feature.spectral_bandwidth(y=y, sr=sr)
rolloff = librosa.feature.spectral_rolloff(y=y, sr=sr)
zero_crossing_rate = librosa.feature.zero_crossing_rate(y)
mfcc = librosa.feature.mfcc(y=y, sr=sr, n_mfcc=20)
tempo, _ = librosa.beat.beat_track(y=y, sr=sr)
harmony, perceptr = librosa.effects.hpss(y)
def compute_statistics(feature):
mean = np.mean(feature)
var = np.var(feature)
return mean, var
chroma_stft_mean, chroma_stft_var = compute_statistics(chroma_stft)
rms_mean, rms_var = compute_statistics(rms)
spectral_centroid_mean, spectral_centroid_var = compute_statistics(spectral_centroid)
spectral_bandwidth_mean, spectral_bandwidth_var = compute_statistics(spectral_bandwidth)
rolloff_mean, rolloff_var = compute_statistics(rolloff)
zero_crossing_rate_mean, zero_crossing_rate_var = compute_statistics(zero_crossing_rate)
harmony_mean, harmony_var = compute_statistics(harmony)
perceptr_mean, perceptr_var = compute_statistics(perceptr)
mfcc_means = np.mean(mfcc, axis=1)
mfcc_vars = np.var(mfcc, axis=1)
# 특성을 데이터프레임으로 변환
features = {
'chroma_stft_mean': [chroma_stft_mean],
'chroma_stft_var': [chroma_stft_var],
'rms_mean': [rms_mean],
'rms_var': [rms_var],
'spectral_centroid_mean': [spectral_centroid_mean],
'spectral_centroid_var': [spectral_centroid_var],
'spectral_bandwidth_mean': [spectral_bandwidth_mean],
'spectral_bandwidth_var': [spectral_bandwidth_var],
'rolloff_mean': [rolloff_mean],
'rolloff_var': [rolloff_var],
'zero_crossing_rate_mean': [zero_crossing_rate_mean],
'zero_crossing_rate_var': [zero_crossing_rate_var],
"harmony_mean": [harmony_mean],
"harmony_var": [harmony_var],
"perceptr_mean": [perceptr_mean],
"perceptr_var": [perceptr_var],
"tempo": [tempo]
}
for i in range(1, 21):
features[f'mfcc{i}_mean'] = [mfcc_means[i-1]]
features[f'mfcc{i}_var'] = [mfcc_vars[i-1]]
df = pd.DataFrame(features)
# 스케일링 (Scaling)
scaled_features = scaler.transform(df.values.reshape(1, -1))
# 텐서로 변환
scaled_tensor = torch.tensor(scaled_features, dtype=torch.float32)
# 예측
model.eval() # 모델을 평가 모드로 설정합니다
with torch.no_grad():
outputs = model.forward(scaled_tensor)
_, preds = torch.max(outputs, 1)
predicted_genre = label_encoder.inverse_transform(preds)[0]
return predicted_genre
audio_path = '/content/spring-mood-wav-212731.wav'
predict_genre_from_audio(audio_path, model, label_encoder, scaler)
/usr/local/lib/python3.10/dist-packages/sklearn/base.py:439: UserWarning: X does not have valid feature names, but MinMaxScaler was fitted with feature names warnings.warn(
'jazz'
스펙토그램 이미지로 분류¶
import os
import librosa
import numpy as np
import torch
from torch.utils.data import Dataset, DataLoader
from torchvision import transforms
class AudioDataset(Dataset):
def __init__(self, file_paths, labels, sample_rate=22050, n_fft=2048, hop_length=512, n_mels=128, duration=5):
self.file_paths = file_paths
self.labels = labels
self.sample_rate = sample_rate
self.n_fft = n_fft
self.hop_length = hop_length
self.n_mels = n_mels
self.duration = duration # 모든 오디오를 동일한 길이로 자르거나 패딩할 길이 (초 단위)
def __len__(self):
return len(self.file_paths)
def __getitem__(self, index):
audio_path = self.file_paths[index]
label = self.labels[index]
# 오디오 파일을 로드하고 스펙트로그램 생성
y, sr = librosa.load(audio_path, sr=self.sample_rate, duration=self.duration)
spectrogram = self.audio_to_spectrogram(y)
# 파이토치 텐서로 변환
spectrogram = torch.FloatTensor(spectrogram)
return spectrogram, label
def audio_to_spectrogram(self, y):
# Compute spectrogram
spectrogram = librosa.feature.melspectrogram(y=y, sr=self.sample_rate, n_fft=self.n_fft, hop_length=self.hop_length, n_mels=self.n_mels)
spectrogram = librosa.power_to_db(spectrogram, ref=np.max) # Convert to dB scale
return spectrogram
def load_data(data_dir, genres):
file_paths = []
labels = []
for genre_idx, genre in enumerate(genres):
genre_dir = os.path.join(data_dir, genre)
for file_name in os.listdir(genre_dir):
if file_name.endswith('.wav'): # WAV 파일 필터링
file_path = os.path.join(genre_dir, file_name)
try:
# 오디오 파일을 로드하여 포맷 에러가 발생하지 않는지 확인
y, sr = librosa.load(file_path)
file_paths.append(file_path)
labels.append(genre_idx)
except Exception as e:
print(f"Error loading {file_path}: {str(e)}")
continue
return file_paths, labels
# 데이터 경로와 장르 설정
data_dir = '/content/Data/genres_original'
genres = 'blues classical country disco hiphop jazz metal pop reggae rock'.split()
# 데이터 로드
file_paths, labels = load_data(data_dir, genres)
# 데이터셋 및 DataLoader 설정
dataset = AudioDataset(file_paths, labels)
train_loader = DataLoader(dataset, batch_size=16, shuffle=True)
<ipython-input-314-280b798a5005>:51: UserWarning: PySoundFile failed. Trying audioread instead. y, sr = librosa.load(file_path) /usr/local/lib/python3.10/dist-packages/librosa/core/audio.py:184: FutureWarning: librosa.core.audio.__audioread_load Deprecated as of librosa version 0.10.0. It will be removed in librosa version 1.0. y, sr_native = __audioread_load(path, offset, duration, dtype)
Error loading /content/Data/genres_original/jazz/jazz.00054.wav:
class AudioCNNLSTM(nn.Module):
def __init__(self, num_classes):
super(AudioCNNLSTM, self).__init__()
# LSTM layers
self.lstm = nn.LSTM(input_size=128, hidden_size=64, num_layers=2, batch_first=True)
# CNN layers
self.conv1 = nn.Conv2d(1, 16, kernel_size=3, stride=1, padding=1)
self.conv2 = nn.Conv2d(16, 32, kernel_size=3, stride=1, padding=1)
self.fc1 = nn.Linear(32 * 56 * 56, 128)
self.fc2 = nn.Linear(128, num_classes)
def forward(self, x):
# LSTM input: (batch_size, seq_length, input_size)
# CNN input: (batch_size, channels, height, width)
x_lstm, _ = self.lstm(x)
# Convert input for CNN
x_cnn = x.unsqueeze(1) # Add channel dimension for CNN input
x_cnn = F.relu(self.conv1(x_cnn))
x_cnn = F.max_pool2d(x_cnn, kernel_size=2, stride=2)
x_cnn = F.relu(self.conv2(x_cnn))
x_cnn = F.max_pool2d(x_cnn, kernel_size=2, stride=2)
x_cnn = x_cnn.view(-1, 32 * 56 * 56) # Flatten for fully connected layers
# Concatenate LSTM and CNN outputs
x_combined = torch.cat((x_lstm[:, -1, :], x_cnn), dim=1)
# Fully connected layers
x = F.relu(self.fc1(x_combined))
x = self.fc2(x)
return x
# 모델 초기화 및 학습 설정
model = AudioCNNLSTM(num_classes=len(genres))
optimizer = optim.Adam(model.parameters(), lr=0.001)
criterion = nn.CrossEntropyLoss()
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model.to(device)
# 학습
epochs = 10
for epoch in range(epochs):
model.train()
sum_losses = 0
sum_accs = 0
for batch_idx, (spectrogram, label) in enumerate(train_loader):
spectrogram = spectrogram.unsqueeze(1).to(device)
label = label.to(device)
optimizer.zero_grad()
output = model(spectrogram)
loss = criterion(output, label)
loss.backward()
optimizer.step()
sum_losses += loss.item()
output_pred_index = torch.argmax(output, axis=1)
acc = (label == output_pred_index).float().mean().item() * 100
sum_accs += acc
avg_loss = sum_losses / len(train_loader)
avg_acc = sum_accs / len(train_loader)
if batch_idx % 10 == 0:
print(f'Epoch {epoch+1}/{epochs}, Batch {batch_idx+1}/{len(train_loader)}, Loss: {loss.item():.4f}, Accuracy: {avg_acc:.2f}%')
# 모델 저장
torch.save(model.state_dict(), '/content/drive/MyDrive/KDT/audio_model.pth')
Epoch 1/10, Batch 1/63, Loss: 2.3100, Accuracy: 0.20% Epoch 1/10, Batch 11/63, Loss: 2.2977, Accuracy: 1.79% Epoch 1/10, Batch 21/63, Loss: 2.3612, Accuracy: 3.57% Epoch 1/10, Batch 31/63, Loss: 2.3090, Accuracy: 5.06% Epoch 1/10, Batch 41/63, Loss: 2.3250, Accuracy: 6.65% Epoch 1/10, Batch 51/63, Loss: 2.2303, Accuracy: 8.83% Epoch 1/10, Batch 61/63, Loss: 2.0360, Accuracy: 12.30% Epoch 2/10, Batch 1/63, Loss: 2.4707, Accuracy: 0.10% Epoch 2/10, Batch 11/63, Loss: 1.9425, Accuracy: 4.27% Epoch 2/10, Batch 21/63, Loss: 2.1176, Accuracy: 8.23% Epoch 2/10, Batch 31/63, Loss: 1.7842, Accuracy: 13.49% Epoch 2/10, Batch 41/63, Loss: 1.6068, Accuracy: 18.75% Epoch 2/10, Batch 51/63, Loss: 1.3172, Accuracy: 24.11% Epoch 2/10, Batch 61/63, Loss: 1.3957, Accuracy: 29.76% Epoch 3/10, Batch 1/63, Loss: 1.7409, Accuracy: 0.60% Epoch 3/10, Batch 11/63, Loss: 1.6368, Accuracy: 6.15% Epoch 3/10, Batch 21/63, Loss: 1.4029, Accuracy: 13.19% Epoch 3/10, Batch 31/63, Loss: 1.4936, Accuracy: 19.44% Epoch 3/10, Batch 41/63, Loss: 1.4290, Accuracy: 26.09% Epoch 3/10, Batch 51/63, Loss: 2.0983, Accuracy: 31.65% Epoch 3/10, Batch 61/63, Loss: 1.4279, Accuracy: 37.10% Epoch 4/10, Batch 1/63, Loss: 1.2811, Accuracy: 1.09% Epoch 4/10, Batch 11/63, Loss: 1.2117, Accuracy: 9.03% Epoch 4/10, Batch 21/63, Loss: 0.9676, Accuracy: 17.56% Epoch 4/10, Batch 31/63, Loss: 1.2126, Accuracy: 24.40% Epoch 4/10, Batch 41/63, Loss: 1.5404, Accuracy: 31.55% Epoch 4/10, Batch 51/63, Loss: 1.6195, Accuracy: 38.29% Epoch 4/10, Batch 61/63, Loss: 1.0158, Accuracy: 44.54% Epoch 5/10, Batch 1/63, Loss: 1.2510, Accuracy: 0.89% Epoch 5/10, Batch 11/63, Loss: 1.4604, Accuracy: 9.13% Epoch 5/10, Batch 21/63, Loss: 1.0545, Accuracy: 19.84% Epoch 5/10, Batch 31/63, Loss: 1.3569, Accuracy: 28.67% Epoch 5/10, Batch 41/63, Loss: 1.0680, Accuracy: 37.60% Epoch 5/10, Batch 51/63, Loss: 1.4138, Accuracy: 46.63% Epoch 5/10, Batch 61/63, Loss: 1.1396, Accuracy: 55.26% Epoch 6/10, Batch 1/63, Loss: 1.1038, Accuracy: 0.79% Epoch 6/10, Batch 11/63, Loss: 0.7358, Accuracy: 12.80% Epoch 6/10, Batch 21/63, Loss: 0.5615, Accuracy: 24.50% Epoch 6/10, Batch 31/63, Loss: 1.3492, Accuracy: 35.52% Epoch 6/10, Batch 41/63, Loss: 0.7026, Accuracy: 46.03% Epoch 6/10, Batch 51/63, Loss: 0.8608, Accuracy: 56.65% Epoch 6/10, Batch 61/63, Loss: 0.7073, Accuracy: 67.86% Epoch 7/10, Batch 1/63, Loss: 0.5982, Accuracy: 1.09% Epoch 7/10, Batch 11/63, Loss: 0.5602, Accuracy: 14.78% Epoch 7/10, Batch 21/63, Loss: 0.5730, Accuracy: 27.78% Epoch 7/10, Batch 31/63, Loss: 0.3951, Accuracy: 41.17% Epoch 7/10, Batch 41/63, Loss: 0.7555, Accuracy: 53.08% Epoch 7/10, Batch 51/63, Loss: 0.6302, Accuracy: 64.48% Epoch 7/10, Batch 61/63, Loss: 0.3383, Accuracy: 77.98% Epoch 8/10, Batch 1/63, Loss: 0.4787, Accuracy: 1.29% Epoch 8/10, Batch 11/63, Loss: 0.3373, Accuracy: 14.88% Epoch 8/10, Batch 21/63, Loss: 0.1983, Accuracy: 29.17% Epoch 8/10, Batch 31/63, Loss: 0.2863, Accuracy: 43.06% Epoch 8/10, Batch 41/63, Loss: 0.3027, Accuracy: 57.24% Epoch 8/10, Batch 51/63, Loss: 0.1577, Accuracy: 71.73% Epoch 8/10, Batch 61/63, Loss: 0.2891, Accuracy: 86.41% Epoch 9/10, Batch 1/63, Loss: 0.1731, Accuracy: 1.59% Epoch 9/10, Batch 11/63, Loss: 0.4984, Accuracy: 15.77% Epoch 9/10, Batch 21/63, Loss: 0.3542, Accuracy: 31.05% Epoch 9/10, Batch 31/63, Loss: 0.0991, Accuracy: 46.43% Epoch 9/10, Batch 41/63, Loss: 0.0787, Accuracy: 61.81% Epoch 9/10, Batch 51/63, Loss: 0.0503, Accuracy: 77.18% Epoch 9/10, Batch 61/63, Loss: 0.0450, Accuracy: 92.36% Epoch 10/10, Batch 1/63, Loss: 0.0863, Accuracy: 1.59% Epoch 10/10, Batch 11/63, Loss: 0.0220, Accuracy: 17.46% Epoch 10/10, Batch 21/63, Loss: 0.1746, Accuracy: 33.13% Epoch 10/10, Batch 31/63, Loss: 0.1291, Accuracy: 48.81% Epoch 10/10, Batch 41/63, Loss: 0.0317, Accuracy: 64.38% Epoch 10/10, Batch 51/63, Loss: 0.1911, Accuracy: 79.76% Epoch 10/10, Batch 61/63, Loss: 0.0359, Accuracy: 95.54%
# 스펙트로그램을 텐서로 변환하는 함수 정의
def audio_to_spectrogram_tensor(audio_path, sample_rate=22050, n_fft=2048, hop_length=512, n_mels=128, duration=5):
# Load audio file with fixed duration
y, sr = librosa.load(audio_path, sr=sample_rate, duration=duration)
# Compute spectrogram
spectrogram = librosa.feature.melspectrogram(y=y, sr=sr, n_fft=n_fft, hop_length=hop_length, n_mels=n_mels)
spectrogram = librosa.power_to_db(spectrogram, ref=np.max) # Convert to dB scale
# Convert spectrogram to tensor
transform = transforms.Compose([
transforms.ToPILImage(), # PIL 이미지로 변환
transforms.Grayscale(num_output_channels=1), # 그레이스케일로 변환
transforms.ToTensor(),
transforms.Normalize(mean=[0.485], std=[0.229]), # 표준화
])
tensor = transform(spectrogram)
return tensor
# 오디오 파일 경로 설정
audio_file = '/content/jazz.00004.wav' # 예제 오디오 파일 경로
# 오디오 파일을 스펙트로그램 텐서로 변환
input_tensor = audio_to_spectrogram_tensor(audio_file)
model = AudioCNNLSTM(num_classes=len(genres))
model.eval()
# GPU 사용 여부 확인 및 설정
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model.to(device)
input_tensor = input_tensor.to(device)
# CNN 모델에 입력하기 위해 데이터 형태 조정
input_tensor = input_tensor.unsqueeze(0) # Add batch dimension for CNN input
# 추론
with torch.no_grad():
output = model(input_tensor)
probabilities = F.softmax(output, dim=1) # 각 클래스에 대한 확률 분포 계산
_, predicted_class = torch.max(output, 1) # 최대 확률을 가진 클래스의 인덱스
# 예측된 장르와 클래스 확률 출력
predicted_genre = genres[predicted_class.item()]
print(f'Predicted Genre: {predicted_genre}')
print(f'Class Probabilities: {probabilities.cpu().numpy()[0]}')
Predicted Genre: jazz Class Probabilities: [0.10388141 0.10554514 0.09810621 0.08924551 0.09312897 0.10791548 0.10385623 0.10160261 0.10388492 0.09283353]
결론¶
정답률은 어느정도 나오나 정확하게 나오지는 않음
오디오나 음악 분류 쪽은 선학습 가중치 데이터가 적어서 찾기가 힘들어 직접 데이터셋을 학습시키거나 다른 도메에서 미세조정을 통해 전이 학습을 해야함
VGGish나 WaveNet과 같은 오디오 분석 특화 모델을 사용하면 정확도가 더 올라갈 것으로 예상됨
오디오 분석에는 여러 방식이 있지만 CNN을 포함한 다양한 딥러닝 기법이 스펙트로그램 데이터를 잘 처리하고 있으며, 다른 방법들보다 높은 정확도와 효율성을 줌으로 현재는 스펙토그램 분석 방식이 가장 널리 사용되고 있다고 함